package com.zsc.wxm.ysh.anjo.house.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.wxm.ysh.anjo.house.cache.ResourcesCacheService;
import com.zsc.wxm.ysh.anjo.house.cache.ViewRequestCacheService;
import com.zsc.wxm.ysh.anjo.house.feign.MailService;
import com.zsc.wxm.ysh.anjo.house.feign.MemberService;
import com.zsc.wxm.ysh.anjo.house.mapper.ViewRequestMapper;
import com.zsc.wxm.ysh.anjo.house.model.Estate;
import com.zsc.wxm.ysh.anjo.house.model.RequestLog;
import com.zsc.wxm.ysh.anjo.house.model.Resources;
import com.zsc.wxm.ysh.anjo.house.model.ViewRequest;
import com.zsc.wxm.ysh.anjo.house.service.*;
import com.zsc.wxm.ysh.anjo.result.CodeEnum;
import com.zsc.wxm.ysh.anjo.result.PageResult;
import com.zsc.wxm.ysh.anjo.result.Result;
import com.zsc.wxm.ysh.anjo.service.base.enums.resources.HouseStatus;
import com.zsc.wxm.ysh.anjo.service.base.enums.resources.ViewRequestStatus;
import com.zsc.wxm.ysh.anjo.service.base.exception.AnjoException;
import com.zsc.wxm.ysh.anjo.service.base.model.member.bo.MemberBO;
import com.zsc.wxm.ysh.anjo.service.base.model.member.dto.ViewRequestDTO;
import com.zsc.wxm.ysh.anjo.service.base.model.member.qo.MemberQuery;
import com.zsc.wxm.ysh.anjo.service.base.model.resources.qo.ViewRequestQuery;
import com.zsc.wxm.ysh.anjo.service.base.model.resources.vo.ImagesVO;
import com.zsc.wxm.ysh.anjo.service.base.model.resources.vo.ViewRequestNoticeVO;
import com.zsc.wxm.ysh.anjo.service.base.model.resources.vo.ViewRequestVO;
import com.zsc.wxm.ysh.anjo.utils.MyDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 描述：
 *
 * @author playmaker
 * @since 2021/2/19 22:55
 */
@Service
@Slf4j
public class ViewRequestServiceImpl extends ServiceImpl<ViewRequestMapper, ViewRequest> implements ViewRequestService {

    @Resource
    private MailService mailService;

    @Resource
    private MemberService memberService;

    @Resource
    private ResourcesService resourcesService;

    @Resource
    private ResourcesCacheService resourcesCacheService;

    @Resource
    private EstateService estateService;

    @Resource
    private RequestLogService requestLogService;

    @Resource
    private ImagesService imagesService;

    @Resource
    private ViewRequestCacheService viewRequestCacheService;


    //TODO 分布式事务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(ViewRequestDTO params) {
        //需要判断该数据是否已存在
        Resources resources = resourcesService.getById(params.getResourcesId());
        if (resources.getStatus().equals(HouseStatus.UNAVAILABLE.getValue())) {
            throw new AnjoException(CodeEnum.HOUSE_IS_UNAVAILABLE);
        }

        isExit(params);

        ViewRequest viewRequest = new ViewRequest();
        BeanUtils.copyProperties(params, viewRequest);
        viewRequest.setRequestTime(LocalDateTime.now());
        viewRequest.setRequestStatus(ViewRequestStatus.WAITE_CONFIRM.getValue());
        baseMapper.insert(viewRequest);

        //日志记录
        RequestLog requestLog = new RequestLog();
        Result rentUserResp = memberService.getOne(MemberQuery.builder().id(params.getRentUserId()).build());
        String rentUserData = JSON.toJSONString(rentUserResp.getData().get("memberBo"));
        MemberBO rentUser = JSON.parseObject(rentUserData, MemberBO.class);
        requestLog.setRentUsername(rentUser.getUsername());
        requestLog.setViewRequestId(viewRequest.getId());
        requestLog.setHostUsername(resources.getConstact());
        requestLog.setDesc(rentUser.getUsername() + "(租客)于" + MyDateUtil.getLocalDateTimeStr(LocalDateTime.now()) + "提交看房申请");
        requestLog.setTitle("提交看房请求");
        log.info("请求日志：{}", requestLog);
        requestLogService.save(requestLog);
        //清除缓存
        viewRequestCacheService.removeList(resources.getId());
        //异步发送邮件
        asyncSendRequestEmail(resources);

    }

    @Async
    public void asyncSendRequestEmail(Resources resources) {
        //发送邮件通知
        Result memberResult = memberService.getOne(MemberQuery.builder()
                .phone(resources.getPhone())
                .limit(1L)
                .build());
        String dataString = JSON.toJSONString(memberResult.getData().get("memberBo"));
        MemberBO memberBo = JSON.parseObject(dataString, MemberBO.class);
        mailService.sendViewRequestEmail(resources.getCode(), memberBo.getEmail());
    }

    private void isExit(ViewRequestDTO params) {
        LambdaQueryWrapper<ViewRequest> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ViewRequest::getRentUserId, params.getRentUserId());
        wrapper.eq(ViewRequest::getResourcesId, params.getResourcesId());
        wrapper.eq(ViewRequest::getViewTime, params.getViewTime());
        wrapper.last("limit 1");
        ViewRequest request = this.getOne(wrapper);
        if (request != null) {
            throw new AnjoException(CodeEnum.REQUEST_IS_EXIT);
        }
    }

    @Override
    public PageResult listPage(ViewRequestQuery query) {
        Page<ViewRequest> page = new Page<>(query.getCurrPage(), query.getPageSize());
        LambdaQueryWrapper<ViewRequest> lambdaQueryWrapper = Wrappers.lambdaQuery();
        //封装条件
        buildQueryWhere(query, lambdaQueryWrapper);
        page = baseMapper.selectPage(page, lambdaQueryWrapper);
        List<ViewRequest> requests = page.getRecords();
        //获取组装结果
        List<ViewRequestVO> requestVos = buildViewRequestVO(requests);
        requestVos = filterViewRequestVos(query, requestVos);
        return PageResult.ok(requestVos, requestVos.size());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(Long id, int status, Long userId) {
        log.info("传入的参数：id={}, status={}, userId={}", id, status, userId);
        LambdaUpdateWrapper<ViewRequest> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(ViewRequest::getId, id).set(ViewRequest::getRequestStatus, status);
        this.update(wrapper);

        ViewRequest request = this.getById(id);

        //记录日志
        RequestLog requestLog = new RequestLog();
        Result hostUserResp = memberService.getOne(MemberQuery.builder().id(userId).build());
        String hostUserData = JSON.toJSONString(hostUserResp.getData().get("memberBo"));
        MemberBO hostUser = JSON.parseObject(hostUserData, MemberBO.class);
        requestLog.setHostUsername(hostUser.getUsername());
        requestLog.setViewRequestId(id);
        if (ViewRequestStatus.WAITE_VIEW.getValue() == status) {
            requestLog.setDesc(hostUser.getUsername() + "(房东)于" + MyDateUtil.getLocalDateTimeStr(LocalDateTime.now()) + "，确认看房申请");
            requestLog.setTitle("房东确认");
        } else if (ViewRequestStatus.CANCELLED.getValue() == status){
            requestLog.setDesc(hostUser.getUsername() + "(房东)于" + MyDateUtil.getLocalDateTimeStr(LocalDateTime.now()) + "，取消看房申请");
            requestLog.setTitle("房东已取消");
        }
        requestLogService.save(requestLog);

        if (ViewRequestStatus.WAITE_VIEW.getValue() == status) {
            //产生一条线下看房的记录
            requestLog = new RequestLog();
            requestLog.setViewRequestId(request.getId());
            requestLog.setDesc("线下看房，时间点：" + MyDateUtil.getLocalDateTimeStr(request.getViewTime()));
            requestLog.setTitle("线下看房");
            requestLogService.save(requestLog);
        }

        //清除缓存
        viewRequestCacheService.listByHouseIds(request.getResourcesId());

    }

    @Override
    public Result deleteRequest(Long id) {
        baseMapper.deleteById(id);
        return Result.ok("删除成功");
    }

    @Override
    public ViewRequestVO viewRequestDetails(Long id) {
        List<ViewRequest> viewRequests = Collections.singletonList(baseMapper.selectById(id));
        List<ViewRequestVO> vos = buildViewRequestVO(viewRequests);
        return vos.get(0);
    }

    @Override
    public List<ViewRequestNoticeVO> getRequestNotice(Long userId) {

        List<Resources> resources = resourcesCacheService.listByUserIdCache(userId);
        if (CollUtil.isEmpty(resources)) {
            return Collections.emptyList();
        }
        //收集houseIds
        List<Long> houseIds = resources.stream().map(Resources::getId).collect(Collectors.toList());
        //根据houseIds获取看房请求数据
        List<ViewRequest> list = new ArrayList<>();
        houseIds.forEach(x -> list.addAll(viewRequestCacheService.listByHouseIds(x)));
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        List<ViewRequest> listAsc = list.stream().sorted(Comparator.comparing(ViewRequest::getRequestStatus))
                .collect(Collectors.toList());
        //获取用户信息
        List<Long> rentUserIds = listAsc.stream().map(ViewRequest::getRentUserId).collect(Collectors.toList());
        //批量获取租客信息
        Result result = memberService.getListByIds(rentUserIds);
        String jsonData = JSON.toJSONString(result.getData().get("list"));
        List<MemberBO> memberBos = JSON.parseArray(jsonData, MemberBO.class);
        HashMap<Long, Resources> map = resources.stream().collect(HashMap::new, (k, v) -> k.put(v.getId(), v), HashMap::putAll);
        List<ViewRequestNoticeVO> vos = new ArrayList<>();
        for (int i = 0;i < listAsc.size();i++) {
            ViewRequestNoticeVO vo = new ViewRequestNoticeVO();
            vo.setId(listAsc.get(i).getId());
            vo.setHouseId(listAsc.get(i).getResourcesId());
            vo.setStatus(ViewRequestStatus.getEnumDesc(listAsc.get(i).getRequestStatus()));
            vo.setTitle(map.get(listAsc.get(i).getResourcesId()).getTitle());
            vo.setPhone(memberBos.get(i).getPhone());
            vo.setUsername(memberBos.get(i).getUsername());
            vo.setRentUserId(memberBos.get(i).getId());
            vo.setDesc(vo.getUsername() + "于" + MyDateUtil.getLocalDateTimeStr(list.get(i).getViewTime()) + "发起看房申请");
            vo.setModifyTime(listAsc.get(i).getModifyTime());
            vos.add(vo);
        }


        return vos;
    }

    @Override
    public List<ViewRequestNoticeVO> getRentUserNotice(Long rentUserId) {
        LambdaQueryWrapper<ViewRequest> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ViewRequest::getRentUserId, rentUserId);
        List<ViewRequest> list = this.list(wrapper);
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        list = list.stream().sorted(Comparator.comparing(ViewRequest::getRequestStatus)).collect(Collectors.toList());
        List<ViewRequestNoticeVO> vos = new ArrayList<>();
        list.forEach(x -> {
            Resources resources = resourcesCacheService.getByIdCache(x.getResourcesId());
            ViewRequestNoticeVO vo = new ViewRequestNoticeVO();
            vo.setStatus(ViewRequestStatus.getEnumDesc(x.getRequestStatus()));
            vo.setModifyTime(x.getModifyTime());
            vo.setTitle(resources.getTitle());
            vos.add(vo);
        });
        return vos;
    }

    /**
     * 封装查询条件
     *
     * @param query 请求参数
     * @param lambdaQueryWrapper 封装条件
     */
    private void buildQueryWhere(ViewRequestQuery query, LambdaQueryWrapper<ViewRequest> lambdaQueryWrapper) {
        if (query.getRequestTimeStart() != null) {
            lambdaQueryWrapper.ge(ViewRequest::getRequestTime, query.getRequestTimeStart());
        }
        if (query.getRequestTimeEnd() != null) {
            lambdaQueryWrapper.le(ViewRequest::getRequestTime, query.getRequestTimeEnd());
        }
        if (query.getViewTimeStart() != null) {
            lambdaQueryWrapper.ge(ViewRequest::getViewTime, query.getViewTimeStart());
        }
        if (query.getViewTimeEnd() != null) {
            lambdaQueryWrapper.le(ViewRequest::getViewTime, query.getViewTimeEnd());
        }
        if (query.getStatus() != null) {
            lambdaQueryWrapper.eq(ViewRequest::getRequestStatus, query.getStatus());
        }
        if (query.getUserId() != null) {
            lambdaQueryWrapper.eq(ViewRequest::getRentUserId, query.getUserId());
        }
    }

    /**
     * 过滤
     *
     * @param query 请求参数
     * @param requestVos vos
     * @return List<ViewRequestVO>
     */
    private List<ViewRequestVO> filterViewRequestVos(ViewRequestQuery query, List<ViewRequestVO> requestVos) {
        //进一步筛选
        if (StringUtils.isNotBlank(query.getProvince())) {
            requestVos = requestVos.stream().filter(x -> x.getProvince().equals(query.getProvince())).collect(Collectors.toList());
        }
        if (StringUtils.isNotBlank(query.getCity())) {
            requestVos = requestVos.stream().filter(x -> x.getCity().equals(query.getCity())).collect(Collectors.toList());
        }
        if (StringUtils.isNotBlank(query.getArea())) {
            requestVos = requestVos.stream().filter(x -> x.getArea().equals(query.getArea())).collect(Collectors.toList());
        }
        if (StringUtils.isNotBlank(query.getEstateName())) {
            requestVos = requestVos.stream().filter(x -> x.getEstateName().contains(query.getEstateName())).collect(Collectors.toList());
        }
        if (StringUtils.isNotBlank(query.getPhone())) {
            requestVos = requestVos.stream().filter(x -> x.getPhone().equals(query.getPhone())).collect(Collectors.toList());
        }
        return requestVos;
    }

    /**
     * 组装VO对象
     *
     * @param requests vos
     * @return List<ViewRequestVO>
     */
    private List<ViewRequestVO> buildViewRequestVO(List<ViewRequest> requests) {
        if (requests.isEmpty()) {
            return Collections.emptyList();
        }
        //组装结果
        List<ViewRequestVO> data = new ArrayList<>();
        //收集房源ids
        List<Long> resourcesIds = requests.stream().map(ViewRequest::getResourcesId).collect(Collectors.toList());
        log.info("houseId={}", resourcesIds);
        List<Resources> resources = new ArrayList<>();
        resourcesIds.forEach(x -> resources.add(resourcesCacheService.getByIdCache(x)));
        //批量获取首页图片
        List<ImagesVO> imagesVos = imagesService.listFirstImage(resourcesIds);
        //楼盘Ids
        List<Long> estateIds = resources.stream().map(Resources::getEstateId).collect(Collectors.toList());
        List<Estate> estates = new ArrayList<>();
        estateIds.forEach(id -> estates.add(estateService.getOneById(id)));
        //用户ids
        List<Long> rentUserIds = requests.stream().map(ViewRequest::getRentUserId).collect(Collectors.toList());
        Result result = memberService.getListByIds(rentUserIds);
        String dataString = JSON.toJSONString(result.getData().get("list"));
        //只需调用一次RPC
        List<MemberBO> memberBos = JSON.parseArray(dataString, MemberBO.class);
        for (int i = 0; i < requests.size(); i++) {
            ViewRequestVO vo = new ViewRequestVO();
            vo.setEstateName(estates.get(i).getName());
            vo.setAddress(estates.get(i).getAddress());
            vo.setCity(estates.get(i).getCity());
            vo.setProvince(estates.get(i).getProvince());
            vo.setArea(estates.get(i).getArea());
            vo.setRentUserName(memberBos.get(i).getUsername());
            vo.setPhone(memberBos.get(i).getPhone());
            vo.setRequestTime(requests.get(i).getRequestTime());
            vo.setStatus(ViewRequestStatus.getEnumDesc(requests.get(i).getRequestStatus()));
            vo.setViewTime(requests.get(i).getViewTime());
            vo.setId(requests.get(i).getId());
            vo.setHouseTitle(resources.get(i).getTitle());
            vo.setHostName(resources.get(i).getConstact());
            vo.setHostPhone(resources.get(i).getPhone());
            vo.setImage(imagesVos.get(i).getImageUrl());
            vo.setRent(resources.get(i).getRent().toString() + "元/月");
            data.add(vo);
        }
        return data;
    }
}
